{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# SKLearn Spacy Reddit Text Classification Example\n",
    "\n",
    "In this example we will be buiding a text classifier using the reddit content moderation dataset.\n",
    "\n",
    "For this, we will be using SpaCy for the word tokenization and lemmatization. \n",
    "\n",
    "The classification will be done with a Logistic Regression binary classifier.\n",
    "\n",
    "The steps in this tutorial include:\n",
    "\n",
    "1) Train and build your NLP model\n",
    "\n",
    "2) Build your containerized model\n",
    "\n",
    "3) Test your model as a docker container\n",
    "\n",
    "4) Run Seldon in your kubernetes cluster\n",
    "\n",
    "5) Deploy your model with Seldon\n",
    "\n",
    "6) Interact with your model through API\n",
    "\n",
    "7) Clean your environment\n",
    "\n",
    "\n",
    "### Before you start\n",
    "Make sure you install the following dependencies, as they are critical for this example to work:\n",
    "\n",
    "* Helm v2.13.1+\n",
    "* A Kubernetes cluster running v1.13 or above (minkube / docker-for-windows work well if enough RAM)\n",
    "* kubectl v1.14+\n",
    "* Python 3.6+\n",
    "* Python DEV requirements (we'll install them below)\n",
    "\n",
    "Let's get started! 🚀🔥\n",
    "\n",
    "## 1) Train and build your NLP model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# Let's first install any dependencies\n",
    "!pip install -r requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd \n",
    "from sklearn.model_selection import train_test_split\n",
    "import numpy as np\n",
    "from sklearn.feature_extraction.text import TfidfVectorizer\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from seldon_core.seldon_client import SeldonClient\n",
    "import dill\n",
    "import sys, os\n",
    "\n",
    "# This import may take a while as it will download the Spacy ENGLISH model\n",
    "from ml_utils import CleanTextTransformer, SpacyTokenTransformer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>prev_idx</th>\n",
       "      <th>parent_idx</th>\n",
       "      <th>body</th>\n",
       "      <th>removed</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>8756</td>\n",
       "      <td>8877</td>\n",
       "      <td>Always be wary of news articles that cite unpu...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>7330</td>\n",
       "      <td>7432</td>\n",
       "      <td>The problem I have with this is that the artic...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>15711</td>\n",
       "      <td>15944</td>\n",
       "      <td>This is indicative of a typical power law, and...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>1604</td>\n",
       "      <td>1625</td>\n",
       "      <td>This doesn't make sense. Chess obviously trans...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>13327</td>\n",
       "      <td>13520</td>\n",
       "      <td>1. I dispute that gene engineering is burdenso...</td>\n",
       "      <td>0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   prev_idx  parent_idx                                               body  \\\n",
       "0      8756        8877  Always be wary of news articles that cite unpu...   \n",
       "1      7330        7432  The problem I have with this is that the artic...   \n",
       "2     15711       15944  This is indicative of a typical power law, and...   \n",
       "3      1604        1625  This doesn't make sense. Chess obviously trans...   \n",
       "4     13327       13520  1. I dispute that gene engineering is burdenso...   \n",
       "\n",
       "   removed  \n",
       "0        0  \n",
       "1        0  \n",
       "2        0  \n",
       "3        0  \n",
       "4        0  "
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "df_cols = [\"prev_idx\", \"parent_idx\", \"body\", \"removed\"]\n",
    "\n",
    "TEXT_COLUMN = \"body\" \n",
    "CLEAN_COLUMN = \"clean_body\"\n",
    "TOKEN_COLUMN = \"token_body\"\n",
    "\n",
    "# Downloading the 50k reddit dataset of moderated comments\n",
    "df = pd.read_csv(\"https://raw.githubusercontent.com/axsauze/reddit-classification-exploration/master/data/reddit_train.csv\", \n",
    "                         names=df_cols, skiprows=1, encoding=\"ISO-8859-1\")\n",
    "\n",
    "df.head()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "<matplotlib.axes._subplots.AxesSubplot at 0x7fcd615502b0>"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Let's see how many examples we have of each class\n",
    "df[\"removed\"].value_counts().plot.bar()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = df[\"body\"].values\n",
    "y = df[\"removed\"].values\n",
    "x_train, x_test, y_train, y_test = train_test_split(\n",
    "    x, y, \n",
    "    stratify=y, \n",
    "    random_state=42, \n",
    "    test_size=0.1, shuffle=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Clean the text\n",
    "clean_text_transformer = CleanTextTransformer()\n",
    "x_train_clean = clean_text_transformer.transform(x_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Tokenize the text and get the lemmas\n",
    "spacy_tokenizer = SpacyTokenTransformer()\n",
    "x_train_tokenized = spacy_tokenizer.transform(x_train_clean)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "TfidfVectorizer(analyzer='word', binary=False, decode_error='strict',\n",
       "        dtype=<class 'numpy.float64'>, encoding='utf-8', input='content',\n",
       "        lowercase=True, max_df=1.0, max_features=10000, min_df=1,\n",
       "        ngram_range=(1, 3), norm='l2',\n",
       "        preprocessor=<function <lambda> at 0x7fcda0a72950>,\n",
       "        smooth_idf=True, stop_words=None, strip_accents=None,\n",
       "        sublinear_tf=False, token_pattern=None,\n",
       "        tokenizer=<function <lambda> at 0x7fcda0a72730>, use_idf=True,\n",
       "        vocabulary=None)"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Build tfidf vectorizer\n",
    "tfidf_vectorizer = TfidfVectorizer(\n",
    "    max_features=10000,\n",
    "    preprocessor=lambda x: x, \n",
    "    tokenizer=lambda x: x, \n",
    "    token_pattern=None,\n",
    "    ngram_range=(1, 3))\n",
    "\n",
    "tfidf_vectorizer.fit(x_train_tokenized)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "# Transform our tokens to tfidf vectors\n",
    "x_train_tfidf = tfidf_vectorizer.transform(\n",
    "    x_train_tokenized)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "LogisticRegression(C=0.1, class_weight=None, dual=False, fit_intercept=True,\n",
       "          intercept_scaling=1, max_iter=100, multi_class='warn',\n",
       "          n_jobs=None, penalty='l2', random_state=None, solver='sag',\n",
       "          tol=0.0001, verbose=0, warm_start=False)"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Train logistic regression classifier\n",
    "lr = LogisticRegression(C=0.1, solver='sag')\n",
    "lr.fit(x_train_tfidf, y_train)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [],
   "source": [
    "# These are the models we'll deploy\n",
    "with open('tfidf_vectorizer.model', 'wb') as model_file:\n",
    "    dill.dump(tfidf_vectorizer, model_file)\n",
    "with open('lr.model', 'wb') as model_file:\n",
    "    dill.dump(lr, model_file)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2) Build your containerized model"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "import dill\r\n",
      "\r\n",
      "from ml_utils import CleanTextTransformer, SpacyTokenTransformer\r\n",
      "\r\n",
      "class RedditClassifier(object):\r\n",
      "    def __init__(self):\r\n",
      "        \r\n",
      "        self._clean_text_transformer = CleanTextTransformer()\r\n",
      "        self._spacy_tokenizer = SpacyTokenTransformer()\r\n",
      "        \r\n",
      "        with open('tfidf_vectorizer.model', 'rb') as model_file:\r\n",
      "            self._tfidf_vectorizer = dill.load(model_file)\r\n",
      "           \r\n",
      "        with open('lr.model', 'rb') as model_file:\r\n",
      "            self._lr_model = dill.load(model_file)\r\n",
      "\r\n",
      "    def predict(self, X, feature_names):\r\n",
      "        clean_text = self._clean_text_transformer.transform(X)\r\n",
      "        spacy_tokens = self._spacy_tokenizer.transform(clean_text)\r\n",
      "        tfidf_features = self._tfidf_vectorizer.transform(spacy_tokens)\r\n",
      "        predictions = self._lr_model.predict_proba(tfidf_features)\r\n",
      "        return predictions\r\n",
      "\r\n"
     ]
    }
   ],
   "source": [
    "# This is the class we will use to deploy\n",
    "!cat RedditClassifier.py"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "['This is the study that the article is based on:\\r\\n\\r\\nhttps://www.nature.com/articles/nature25778.epdf']\n",
      "[[0.82767095 0.17232905]]\n"
     ]
    }
   ],
   "source": [
    "# test that our model works\n",
    "from RedditClassifier import RedditClassifier\n",
    "# With one sample\n",
    "sample = x_test[0:1]\n",
    "print(sample)\n",
    "print(RedditClassifier().predict(sample, [\"feature_name\"]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Create Docker Image with the S2i utility\n",
    "Using the S2I command line interface we wrap our current model to seve it through the Seldon interface"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MODEL_NAME=RedditClassifier\r\n",
      "API_TYPE=REST\r\n",
      "SERVICE_TYPE=MODEL\r\n",
      "PERSISTENCE=0\r\n"
     ]
    }
   ],
   "source": [
    "# To create a docker image we need to create the .s2i folder configuration as below:\n",
    "!cat .s2i/environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "scipy>= 0.13.3\r\n",
      "scikit-learn>=0.18\r\n",
      "spacy==2.0.18\r\n",
      "dill==0.2.9\r\n",
      "seldon-core==0.2.7\r\n"
     ]
    }
   ],
   "source": [
    "# As well as a requirements.txt file with all the relevant dependencies\n",
    "!cat requirements.txt"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "---> Installing application source...\n",
      "---> Installing dependencies ...\n",
      "Looking in links: /whl\n",
      "Collecting scipy>=0.13.3 (from -r requirements.txt (line 1))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/72/4c/5f81e7264b0a7a8bd570810f48cd346ba36faedbd2ba255c873ad556de76/scipy-1.3.0-cp36-cp36m-manylinux1_x86_64.whl (25.2MB)\n",
      "Collecting scikit-learn>=0.18 (from -r requirements.txt (line 2))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/85/04/49633f490f726da6e454fddc8e938bbb5bfed2001681118d3814c219b723/scikit_learn-0.21.2-cp36-cp36m-manylinux1_x86_64.whl (6.7MB)\n",
      "Collecting spacy==2.0.18 (from -r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ae/6e/a89da6b5c83f8811e46e3a9270c1aed90e9b9ee6c60faf52b7239e5d3d69/spacy-2.0.18-cp36-cp36m-manylinux1_x86_64.whl (25.2MB)\n",
      "Collecting dill==0.2.9 (from -r requirements.txt (line 4))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/fe/42/bfe2e0857bc284cbe6a011d93f2a9ad58a22cb894461b199ae72cfef0f29/dill-0.2.9.tar.gz (150kB)\n",
      "Requirement already satisfied: seldon-core==0.2.7 in /usr/local/lib/python3.6/site-packages (from -r requirements.txt (line 5)) (0.2.7)\n",
      "Requirement already satisfied: numpy>=1.13.3 in /usr/local/lib/python3.6/site-packages (from scipy>=0.13.3->-r requirements.txt (line 1)) (1.16.3)\n",
      "Collecting joblib>=0.11 (from scikit-learn>=0.18->-r requirements.txt (line 2))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/cd/c1/50a758e8247561e58cb87305b1e90b171b8c767b15b12a1734001f41d356/joblib-0.13.2-py2.py3-none-any.whl (278kB)\n",
      "Collecting murmurhash<1.1.0,>=0.28.0 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/a6/e6/63f160a4fdf0e875d16b28f972083606d8d54f56cd30cb8929f9a1ee700e/murmurhash-1.0.2-cp36-cp36m-manylinux1_x86_64.whl\n",
      "Collecting thinc<6.13.0,>=6.12.1 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/db/a7/46640a46fd707aeb204aa4257a70974b6a22a0204ba703164d803215776f/thinc-6.12.1-cp36-cp36m-manylinux1_x86_64.whl (1.9MB)\n",
      "Collecting plac<1.0.0,>=0.9.6 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/9e/9b/62c60d2f5bc135d2aa1d8c8a86aaf84edb719a59c7f11a4316259e61a298/plac-0.9.6-py2.py3-none-any.whl\n",
      "Collecting ujson>=1.35 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/16/c4/79f3409bc710559015464e5f49b9879430d8f87498ecdc335899732e5377/ujson-1.35.tar.gz (192kB)\n",
      "Collecting regex==2018.01.10 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/76/f4/7146c3812f96fcaaf2d06ff6862582302626a59011ccb6f2833bb38d80f7/regex-2018.01.10.tar.gz (612kB)\n",
      "Requirement already satisfied: requests<3.0.0,>=2.13.0 in /usr/local/lib/python3.6/site-packages (from spacy==2.0.18->-r requirements.txt (line 3)) (2.21.0)\n",
      "Collecting preshed<2.1.0,>=2.0.1 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/20/93/f222fb957764a283203525ef20e62008675fd0a14ffff8cc1b1490147c63/preshed-2.0.1-cp36-cp36m-manylinux1_x86_64.whl (83kB)\n",
      "Collecting cymem<2.1.0,>=2.0.2 (from spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/3d/61/9b0520c28eb199a4b1ca667d96dd625bba003c14c75230195f9691975f85/cymem-2.0.2-cp36-cp36m-manylinux1_x86_64.whl\n",
      "Requirement already satisfied: jaeger-client in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (4.0.0)\n",
      "Requirement already satisfied: Flask-OpenTracing==0.2.0 in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (0.2.0)\n",
      "Requirement already satisfied: grpcio in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.20.1)\n",
      "Requirement already satisfied: tensorflow in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.13.1)\n",
      "Requirement already satisfied: grpcio-opentracing in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.1.4)\n",
      "Requirement already satisfied: flatbuffers in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.11)\n",
      "Requirement already satisfied: pyyaml in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (5.1)\n",
      "Requirement already satisfied: protobuf in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (3.7.1)\n",
      "Requirement already satisfied: opentracing<2,>=1.2.2 in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.3.0)\n",
      "Requirement already satisfied: flask-cors in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (3.0.7)\n",
      "Requirement already satisfied: flask in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (1.0.2)\n",
      "Requirement already satisfied: redis in /usr/local/lib/python3.6/site-packages (from seldon-core==0.2.7->-r requirements.txt (line 5)) (3.2.1)\n",
      "Collecting tqdm<5.0.0,>=4.10.0 (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/45/af/685bf3ce889ea191f3b916557f5677cc95a5e87b2fa120d74b5dd6d049d0/tqdm-4.32.1-py2.py3-none-any.whl (49kB)\n",
      "Collecting msgpack<0.6.0,>=0.5.6 (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/22/4e/dcf124fd97e5f5611123d6ad9f40ffd6eb979d1efdc1049e28a795672fcd/msgpack-0.5.6-cp36-cp36m-manylinux1_x86_64.whl (315kB)\n",
      "Requirement already satisfied: six<2.0.0,>=1.10.0 in /usr/local/lib/python3.6/site-packages (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3)) (1.12.0)\n",
      "Collecting wrapt<1.11.0,>=1.10.0 (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/a0/47/66897906448185fcb77fc3c2b1bc20ed0ecca81a0f2f88eda3fc5a34fc3d/wrapt-1.10.11.tar.gz\n",
      "Collecting msgpack-numpy<0.4.4 (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/ad/45/464be6da85b5ca893cfcbd5de3b31a6710f636ccb8521b17bd4110a08d94/msgpack_numpy-0.4.3.2-py2.py3-none-any.whl\n",
      "Collecting cytoolz<0.10,>=0.9.0 (from thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/36/f4/9728ba01ccb2f55df9a5af029b48ba0aaca1081bbd7823ea2ee223ba7a42/cytoolz-0.9.0.1.tar.gz (443kB)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Requirement already satisfied: certifi>=2017.4.17 in /usr/local/lib/python3.6/site-packages (from requests<3.0.0,>=2.13.0->spacy==2.0.18->-r requirements.txt (line 3)) (2019.3.9)\n",
      "Requirement already satisfied: urllib3<1.25,>=1.21.1 in /usr/local/lib/python3.6/site-packages (from requests<3.0.0,>=2.13.0->spacy==2.0.18->-r requirements.txt (line 3)) (1.24.2)\n",
      "Requirement already satisfied: idna<2.9,>=2.5 in /usr/local/lib/python3.6/site-packages (from requests<3.0.0,>=2.13.0->spacy==2.0.18->-r requirements.txt (line 3)) (2.8)\n",
      "Requirement already satisfied: chardet<3.1.0,>=3.0.2 in /usr/local/lib/python3.6/site-packages (from requests<3.0.0,>=2.13.0->spacy==2.0.18->-r requirements.txt (line 3)) (3.0.4)\n",
      "Requirement already satisfied: threadloop<2,>=1 in /usr/local/lib/python3.6/site-packages (from jaeger-client->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.0.2)\n",
      "Requirement already satisfied: tornado<5,>=4.3 in /usr/local/lib/python3.6/site-packages (from jaeger-client->seldon-core==0.2.7->-r requirements.txt (line 5)) (4.5.3)\n",
      "Requirement already satisfied: thrift in /usr/local/lib/python3.6/site-packages (from jaeger-client->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.11.0)\n",
      "Requirement already satisfied: termcolor>=1.1.0 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.1.0)\n",
      "Requirement already satisfied: keras-preprocessing>=1.0.5 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.0.9)\n",
      "Requirement already satisfied: wheel>=0.26 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.33.1)\n",
      "Requirement already satisfied: keras-applications>=1.0.6 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.0.7)\n",
      "Requirement already satisfied: absl-py>=0.1.6 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.7.1)\n",
      "Requirement already satisfied: tensorflow-estimator<1.14.0rc0,>=1.13.0 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.13.0)\n",
      "Requirement already satisfied: astor>=0.6.0 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.7.1)\n",
      "Requirement already satisfied: tensorboard<1.14.0,>=1.13.0 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.13.1)\n",
      "Requirement already satisfied: gast>=0.2.0 in /usr/local/lib/python3.6/site-packages (from tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.2.2)\n",
      "Requirement already satisfied: setuptools in /usr/local/lib/python3.6/site-packages (from protobuf->seldon-core==0.2.7->-r requirements.txt (line 5)) (41.0.1)\n",
      "Requirement already satisfied: itsdangerous>=0.24 in /usr/local/lib/python3.6/site-packages (from flask->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.1.0)\n",
      "Requirement already satisfied: click>=5.1 in /usr/local/lib/python3.6/site-packages (from flask->seldon-core==0.2.7->-r requirements.txt (line 5)) (7.0)\n",
      "Requirement already satisfied: Jinja2>=2.10 in /usr/local/lib/python3.6/site-packages (from flask->seldon-core==0.2.7->-r requirements.txt (line 5)) (2.10.1)\n",
      "Requirement already satisfied: Werkzeug>=0.14 in /usr/local/lib/python3.6/site-packages (from flask->seldon-core==0.2.7->-r requirements.txt (line 5)) (0.15.2)\n",
      "Collecting toolz>=0.8.0 (from cytoolz<0.10,>=0.9.0->thinc<6.13.0,>=6.12.1->spacy==2.0.18->-r requirements.txt (line 3))\n",
      "  WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "Downloading https://files.pythonhosted.org/packages/14/d0/a73c15bbeda3d2e7b381a36afb0d9cd770a9f4adc5d1532691013ba881db/toolz-0.9.0.tar.gz (45kB)\n",
      "Requirement already satisfied: h5py in /usr/local/lib/python3.6/site-packages (from keras-applications>=1.0.6->tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (2.9.0)\n",
      "Requirement already satisfied: mock>=2.0.0 in /usr/local/lib/python3.6/site-packages (from tensorflow-estimator<1.14.0rc0,>=1.13.0->tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (2.0.0)\n",
      "Requirement already satisfied: markdown>=2.6.8 in /usr/local/lib/python3.6/site-packages (from tensorboard<1.14.0,>=1.13.0->tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (3.1)\n",
      "Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib/python3.6/site-packages (from Jinja2>=2.10->flask->seldon-core==0.2.7->-r requirements.txt (line 5)) (1.1.1)\n",
      "Requirement already satisfied: pbr>=0.11 in /usr/local/lib/python3.6/site-packages (from mock>=2.0.0->tensorflow-estimator<1.14.0rc0,>=1.13.0->tensorflow->seldon-core==0.2.7->-r requirements.txt (line 5)) (5.2.0)\n",
      "Building wheels for collected packages: dill, ujson, regex, wrapt, cytoolz, toolz\n",
      "Building wheel for dill (setup.py): started\n",
      "Building wheel for dill (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/5b/d7/0f/e58eae695403de585269f4e4a94e0cd6ca60ec0c202936fa4a\n",
      "Building wheel for ujson (setup.py): started\n",
      "Building wheel for ujson (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/28/77/e4/0311145b9c2e2f01470e744855131f9e34d6919687550f87d1\n",
      "Building wheel for regex (setup.py): started\n",
      "Building wheel for regex (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/74/17/3f/c77bba99efd74ba1a19862c9dd97f4b6d735e2826721dc00ff\n",
      "Building wheel for wrapt (setup.py): started\n",
      "Building wheel for wrapt (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/48/5d/04/22361a593e70d23b1f7746d932802efe1f0e523376a74f321e\n",
      "Building wheel for cytoolz (setup.py): started\n",
      "Building wheel for cytoolz (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/88/f3/11/9817b001e59ab04889e8cffcbd9087e2e2155b9ebecfc8dd38\n",
      "Building wheel for toolz (setup.py): started\n",
      "Building wheel for toolz (setup.py): finished with status 'done'\n",
      "Stored in directory: /root/.cache/pip/wheels/f4/0c/f6/ce6b2d1aa459ee97cc3c0f82236302bd62d89c86c700219463\n",
      "Successfully built dill ujson regex wrapt cytoolz toolz\n",
      "Installing collected packages: scipy, joblib, scikit-learn, murmurhash, tqdm, plac, msgpack, dill, wrapt, msgpack-numpy, cymem, preshed, toolz, cytoolz, thinc, ujson, regex, spacy\n",
      "Successfully installed cymem-2.0.2 cytoolz-0.9.0.1 dill-0.2.9 joblib-0.13.2 msgpack-0.5.6 msgpack-numpy-0.4.3.2 murmurhash-1.0.2 plac-0.9.6 preshed-2.0.1 regex-2018.1.10 scikit-learn-0.21.2 scipy-1.3.0 spacy-2.0.18 thinc-6.12.1 toolz-0.9.0 tqdm-4.32.1 ujson-1.35 wrapt-1.10.11\n",
      "WARNING: Url '/whl' is ignored. It is either a non-existing path or lacks a specific scheme.\n",
      "WARNING: You are using pip version 19.1, however version 19.1.1 is available.\n",
      "You should consider upgrading via the 'pip install --upgrade pip' command.\n",
      "Build completed successfully\n"
     ]
    }
   ],
   "source": [
    "!s2i build . seldonio/seldon-core-s2i-python3:0.13 reddit-classifier:0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3) Test your model as a docker container"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Error: No such container: reddit_predictor\r\n"
     ]
    }
   ],
   "source": [
    "# Remove previously deployed containers for this model\n",
    "!docker rm -f reddit_predictor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "be29c6a00adec0f708dc5a1c83613e0656fddc06daba4ca02d93b5a7ece9b92b\r\n"
     ]
    }
   ],
   "source": [
    "!docker run --name \"reddit_predictor\" -d --rm -p 5001:5000 reddit-classifier:0.1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Make sure you wait for language model\n",
    "SpaCy will download the English language model, so you have to make sure the container finishes downloading it before it can be used. You can view this by running the logs until you see \"Linking successful\"."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2019-05-27T13:50:12.739381600Z starting microservice\n",
      "2019-05-27T13:50:14.023399000Z 2019-05-27 13:50:14,023 - seldon_core.microservice:main:154 - INFO:  Starting microservice.py:main\n",
      "2019-05-27T13:50:14.024836400Z 2019-05-27 13:50:14,024 - seldon_core.microservice:main:185 - INFO:  Annotations: {}\n",
      "2019-05-27T13:50:14.686919400Z Collecting en_core_web_sm==2.0.0 from https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.0.0/en_core_web_sm-2.0.0.tar.gz#egg=en_core_web_sm==2.0.0\n",
      "2019-05-27T13:50:15.402484400Z   Downloading https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-2.0.0/en_core_web_sm-2.0.0.tar.gz (37.4MB)\n",
      "2019-05-27T13:50:47.771818100Z Building wheels for collected packages: en-core-web-sm\n",
      "2019-05-27T13:50:47.772287600Z   Building wheel for en-core-web-sm (setup.py): started\n",
      "2019-05-27T13:50:49.845376700Z   Building wheel for en-core-web-sm (setup.py): finished with status 'done'\n",
      "2019-05-27T13:50:49.845641500Z   Stored in directory: /tmp/pip-ephem-wheel-cache-wszfsf1z/wheels/54/7c/d8/f86364af8fbba7258e14adae115f18dd2c91552406edc3fdaa\n",
      "2019-05-27T13:50:50.163985100Z Successfully built en-core-web-sm\n",
      "2019-05-27T13:50:50.164057000Z Installing collected packages: en-core-web-sm\n",
      "2019-05-27T13:50:50.242852700Z Successfully installed en-core-web-sm-2.0.0\n",
      "2019-05-27T13:50:50.400850200Z WARNING: You are using pip version 19.1, however version 19.1.1 is available.\n",
      "2019-05-27T13:50:50.400901100Z You should consider upgrading via the 'pip install --upgrade pip' command.\n",
      "2019-05-27T13:50:51.728895100Z --- Logging error ---\n",
      "2019-05-27T13:50:51.728944900Z Traceback (most recent call last):\n",
      "2019-05-27T13:50:51.728954200Z   File \"/usr/local/lib/python3.6/logging/__init__.py\", line 994, in emit\n",
      "2019-05-27T13:50:51.728958900Z     msg = self.format(record)\n",
      "2019-05-27T13:50:51.728963000Z   File \"/usr/local/lib/python3.6/logging/__init__.py\", line 840, in format\n",
      "2019-05-27T13:50:51.728966900Z     return fmt.format(record)\n",
      "2019-05-27T13:50:51.728970500Z   File \"/usr/local/lib/python3.6/logging/__init__.py\", line 577, in format\n",
      "2019-05-27T13:50:51.728974300Z     record.message = record.getMessage()\n",
      "2019-05-27T13:50:51.728977900Z   File \"/usr/local/lib/python3.6/logging/__init__.py\", line 338, in getMessage\n",
      "2019-05-27T13:50:51.728981600Z     msg = msg % self.args\n",
      "2019-05-27T13:50:51.728985100Z TypeError: not all arguments converted during string formatting\n",
      "2019-05-27T13:50:51.728988800Z Call stack:\n",
      "2019-05-27T13:50:51.728992300Z   File \"/usr/local/bin/seldon-core-microservice\", line 10, in <module>\n",
      "2019-05-27T13:50:51.728996500Z     sys.exit(main())\n",
      "2019-05-27T13:50:51.729000000Z   File \"/usr/local/lib/python3.6/site-packages/seldon_core/microservice.py\", line 189, in main\n",
      "2019-05-27T13:50:51.729004000Z     logger.info(\"Importing \",args.interface_name)\n",
      "2019-05-27T13:50:51.729007800Z Message: 'Importing '\n",
      "2019-05-27T13:50:51.729011400Z Arguments: ('RedditClassifier',)\n",
      "2019-05-27T13:50:51.729025900Z /usr/local/lib/python3.6/site-packages/sklearn/base.py:306: UserWarning: Trying to unpickle estimator TfidfTransformer from version 0.20.3 when using version 0.21.2. This might lead to breaking code or invalid results. Use at your own risk.\n",
      "2019-05-27T13:50:51.729030000Z   UserWarning)\n",
      "2019-05-27T13:50:51.729033400Z /usr/local/lib/python3.6/site-packages/sklearn/base.py:306: UserWarning: Trying to unpickle estimator TfidfVectorizer from version 0.20.3 when using version 0.21.2. This might lead to breaking code or invalid results. Use at your own risk.\n",
      "2019-05-27T13:50:51.729036900Z   UserWarning)\n",
      "2019-05-27T13:50:51.729040100Z /usr/local/lib/python3.6/site-packages/sklearn/base.py:306: UserWarning: Trying to unpickle estimator LogisticRegression from version 0.20.3 when using version 0.21.2. This might lead to breaking code or invalid results. Use at your own risk.\n",
      "2019-05-27T13:50:51.729044000Z   UserWarning)\n",
      "2019-05-27T13:50:51.729047500Z 2019-05-27 13:50:51,727 - seldon_core.microservice:main:226 - INFO:  REST microservice running on port 5000\n",
      "2019-05-27T13:50:51.729051200Z 2019-05-27 13:50:51,728 - seldon_core.microservice:main:260 - INFO:  Starting servers\n",
      "2019-05-27T13:50:51.730423900Z \n",
      "2019-05-27T13:50:51.730464700Z \u001b[93m    Linking successful\u001b[0m\n",
      "2019-05-27T13:50:51.730473700Z     /usr/local/lib/python3.6/site-packages/en_core_web_sm -->\n",
      "2019-05-27T13:50:51.730477700Z     /usr/local/lib/python3.6/site-packages/spacy/data/en_core_web_sm\n",
      "2019-05-27T13:50:51.730481100Z \n",
      "2019-05-27T13:50:51.730484300Z     You can now load the model via spacy.load('en_core_web_sm')\n",
      "2019-05-27T13:50:51.730487600Z \n",
      "2019-05-27T13:50:51.743475000Z  * Serving Flask app \"seldon_core.wrapper\" (lazy loading)\n",
      "2019-05-27T13:50:51.743530400Z  * Environment: production\n",
      "2019-05-27T13:50:51.743538900Z    WARNING: Do not use the development server in a production environment.\n",
      "2019-05-27T13:50:51.743542800Z    Use a production WSGI server instead.\n",
      "2019-05-27T13:50:51.743546000Z  * Debug mode: off\n",
      "2019-05-27T13:50:51.760002000Z 2019-05-27 13:50:51,759 - werkzeug:_log:122 - INFO:   * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)\n",
      "^C\n"
     ]
    }
   ],
   "source": [
    "# Here we need to wait until we see \"Linking successful\", as it's downloading the Spacy English model\n",
    "# You can hit stop when this happens\n",
    "!docker logs -t -f reddit_predictor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Success:True message:\n",
      "Request:\n",
      "data {\n",
      "  names: \"tfidf\"\n",
      "  ndarray {\n",
      "    values {\n",
      "      string_value: \"This is the study that the article is based on:\\r\\n\\r\\nhttps://www.nature.com/articles/nature25778.epdf\"\n",
      "    }\n",
      "  }\n",
      "}\n",
      "\n",
      "Response:\n",
      "meta {\n",
      "}\n",
      "data {\n",
      "  names: \"t:0\"\n",
      "  names: \"t:1\"\n",
      "  ndarray {\n",
      "    values {\n",
      "      list_value {\n",
      "        values {\n",
      "          number_value: 0.8276709475641506\n",
      "        }\n",
      "        values {\n",
      "          number_value: 0.1723290524358494\n",
      "        }\n",
      "      }\n",
      "    }\n",
      "  }\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "# We now test the REST endpoint expecting the same result\n",
    "endpoint = \"0.0.0.0:5001\"\n",
    "batch = sample\n",
    "payload_type = \"ndarray\"\n",
    "\n",
    "sc = SeldonClient(microservice_endpoint=endpoint)\n",
    "response = sc.microservice(\n",
    "    data=batch,\n",
    "    method=\"predict\",\n",
    "    payload_type=payload_type,\n",
    "    names=[\"tfidf\"])\n",
    "\n",
    "print(response)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "reddit_predictor\r\n"
     ]
    }
   ],
   "source": [
    "# We now stop it to run it in docker\n",
    "!docker stop reddit_predictor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4) Run Seldon in your kubernetes cluster\n",
    "In order to run Seldon we need to make sure that Helm is initialised and Tiller is running. \n",
    "\n",
    "For this we can run the following initialisation and waiting commands."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "clusterrolebinding.rbac.authorization.k8s.io/kube-system-cluster-admin created\n",
      "$HELM_HOME has been configured at /home/alejandro/.helm.\n",
      "Warning: Tiller is already installed in the cluster.\n",
      "(Use --client-only to suppress this message, or --upgrade to upgrade Tiller to the current version.)\n",
      "Happy Helming!\n",
      "deployment \"tiller-deploy\" successfully rolled out\n"
     ]
    }
   ],
   "source": [
    "# If not running you can install it\n",
    "# First initialise helm\n",
    "!kubectl create clusterrolebinding kube-system-cluster-admin --clusterrole=cluster-admin --serviceaccount=kube-system:default\n",
    "!helm init\n",
    "!kubectl rollout status deploy/tiller-deploy -n kube-system"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can install run the Seldon Operator using the latest Helm charts"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   seldon-core-operator\n",
      "LAST DEPLOYED: Mon May 27 15:04:30 2019\n",
      "NAMESPACE: default\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1/ClusterRole\n",
      "NAME                          AGE\n",
      "seldon-operator-manager-role  0s\n",
      "\n",
      "==> v1/ClusterRoleBinding\n",
      "NAME                                 AGE\n",
      "seldon-operator-manager-rolebinding  0s\n",
      "\n",
      "==> v1/Pod(related)\n",
      "NAME                                  READY  STATUS             RESTARTS  AGE\n",
      "seldon-operator-controller-manager-0  0/1    ContainerCreating  0         0s\n",
      "\n",
      "==> v1/Secret\n",
      "NAME                                   TYPE    DATA  AGE\n",
      "seldon-operator-webhook-server-secret  Opaque  0     0s\n",
      "\n",
      "==> v1/Service\n",
      "NAME                                        TYPE       CLUSTER-IP      EXTERNAL-IP  PORT(S)  AGE\n",
      "seldon-operator-controller-manager-service  ClusterIP  10.101.147.136  <none>       443/TCP  0s\n",
      "\n",
      "==> v1/StatefulSet\n",
      "NAME                                READY  AGE\n",
      "seldon-operator-controller-manager  0/1    0s\n",
      "\n",
      "==> v1beta1/CustomResourceDefinition\n",
      "NAME                                         AGE\n",
      "seldondeployments.machinelearning.seldon.io  0s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "NOTES: TODO\n",
      "\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install seldon-core-operator --name seldon-core-operator --repo https://storage.googleapis.com/seldon-charts"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And we can make sure that it is actually running with the following command "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "seldon-operator-controller-manager-0   1/1     Running   1          12s\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl get pod | grep seldon"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order for us to be able to reach the model, we will need to set up an ingress. For this we will use ambassador:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME:   ambassador\n",
      "LAST DEPLOYED: Mon May 27 15:04:50 2019\n",
      "NAMESPACE: default\n",
      "STATUS: DEPLOYED\n",
      "\n",
      "RESOURCES:\n",
      "==> v1/Deployment\n",
      "NAME        READY  UP-TO-DATE  AVAILABLE  AGE\n",
      "ambassador  0/3    3           0          0s\n",
      "\n",
      "==> v1/Pod(related)\n",
      "NAME                         READY  STATUS             RESTARTS  AGE\n",
      "ambassador-7bfc87f865-jkxs8  0/1    ContainerCreating  0         0s\n",
      "ambassador-7bfc87f865-nr7bn  0/1    ContainerCreating  0         0s\n",
      "ambassador-7bfc87f865-q4lng  0/1    ContainerCreating  0         0s\n",
      "\n",
      "==> v1/Service\n",
      "NAME               TYPE          CLUSTER-IP     EXTERNAL-IP  PORT(S)                     AGE\n",
      "ambassador         LoadBalancer  10.101.89.32   localhost    80:30004/TCP,443:31350/TCP  0s\n",
      "ambassador-admins  ClusterIP     10.98.228.159  <none>       8877/TCP                    0s\n",
      "\n",
      "==> v1/ServiceAccount\n",
      "NAME        SECRETS  AGE\n",
      "ambassador  1        1s\n",
      "\n",
      "==> v1beta1/ClusterRole\n",
      "NAME        AGE\n",
      "ambassador  1s\n",
      "\n",
      "==> v1beta1/ClusterRoleBinding\n",
      "NAME        AGE\n",
      "ambassador  1s\n",
      "\n",
      "\n",
      "NOTES:\n",
      "Congratuations! You've successfully installed Ambassador.\n",
      "\n",
      "For help, visit our Slack at https://d6e.co/slack or view the documentation online at https://www.getambassador.io.\n",
      "\n",
      "To get the IP address of Ambassador, run the following commands:\n",
      "NOTE: It may take a few minutes for the LoadBalancer IP to be available.\n",
      "     You can watch the status of by running 'kubectl get svc -w  --namespace default ambassador'\n",
      "\n",
      "  On GKE/Azure:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace default ambassador -o jsonpath='{.status.loadBalancer.ingress[0].ip}')\n",
      "\n",
      "  On AWS:\n",
      "  export SERVICE_IP=$(kubectl get svc --namespace default ambassador -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')\n",
      "\n",
      "  echo http://$SERVICE_IP:\n",
      "\n"
     ]
    }
   ],
   "source": [
    "!helm install stable/ambassador --name ambassador --set crds.keep=false"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can now see the ambassador service is running. In our case we can reach it through the external IP which is our localhost, but if you are using a cloud provider, make sure you have access to the ambassador endpoint."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 58,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME         TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE\r\n",
      "ambassador   LoadBalancer   10.101.89.32   localhost     80:30004/TCP,443:31350/TCP   2m43s\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl get svc ambassador"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5) Deploy your model with Seldon\n",
    "We can now deploy our model by using the Seldon graph definition:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 60,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\r\n",
      "    \"apiVersion\": \"machinelearning.seldon.io/v1alpha2\",\r\n",
      "    \"kind\": \"SeldonDeployment\",\r\n",
      "    \"metadata\": {\r\n",
      "        \"labels\": {\r\n",
      "            \"app\": \"seldon\"\r\n",
      "        },\r\n",
      "        \"name\": \"reddit-classifier\"\r\n",
      "    },\r\n",
      "    \"spec\": {\r\n",
      "        \"annotations\": {\r\n",
      "            \"project_name\": \"Reddit classifier\",\r\n",
      "            \"deployment_version\": \"v1\"\r\n",
      "        },\r\n",
      "        \"name\": \"reddit-classifier\",\r\n",
      "        \"oauth_key\": \"oauth-key\",\r\n",
      "        \"oauth_secret\": \"oauth-secret\",\r\n",
      "        \"predictors\": [\r\n",
      "            {\r\n",
      "                \"componentSpecs\": [{\r\n",
      "                    \"spec\": {\r\n",
      "                        \"containers\": [\r\n",
      "                            {\r\n",
      "                                \"image\": \"reddit-classifier:0.1\",\r\n",
      "                                \"imagePullPolicy\": \"IfNotPresent\",\r\n",
      "                                \"name\": \"classifier\",\r\n",
      "                                \"resources\": {\r\n",
      "                                    \"requests\": {\r\n",
      "                                        \"memory\": \"1Mi\"\r\n",
      "                                    }\r\n",
      "                                }\r\n",
      "                            }\r\n",
      "                        ],\r\n",
      "                        \"terminationGracePeriodSeconds\": 20\r\n",
      "                    }\r\n",
      "                }],\r\n",
      "                \"graph\": {\r\n",
      "                    \"children\": [],\r\n",
      "                    \"name\": \"classifier\",\r\n",
      "                    \"endpoint\": {\r\n",
      "            \"type\" : \"REST\"\r\n",
      "            },\r\n",
      "                    \"type\": \"MODEL\"\r\n",
      "                },\r\n",
      "                \"name\": \"single-model\",\r\n",
      "                \"replicas\": 1,\r\n",
      "        \"annotations\": {\r\n",
      "            \"predictor_version\" : \"v1\"\r\n",
      "        }\r\n",
      "            }\r\n",
      "        ]\r\n",
      "    }\r\n",
      "}\r\n"
     ]
    }
   ],
   "source": [
    "# We'll use our seldon deployment file\n",
    "!cat reddit_clf.json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "seldondeployment.machinelearning.seldon.io/reddit-classifier created\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl apply -f reddit_clf.json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "NAME                                                    READY   STATUS    RESTARTS   AGE\r\n",
      "ambassador-7bfc87f865-jkxs8                             1/1     Running   0          5m2s\r\n",
      "ambassador-7bfc87f865-nr7bn                             1/1     Running   0          5m2s\r\n",
      "ambassador-7bfc87f865-q4lng                             1/1     Running   0          5m2s\r\n",
      "reddit-classifier-single-model-9199e4b-bcc5cdcc-g8j2q   2/2     Running   1          77s\r\n",
      "seldon-operator-controller-manager-0                    1/1     Running   1          5m23s\r\n"
     ]
    }
   ],
   "source": [
    "!kubectl get pods "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6) Interact with your model through API\n",
    "Now that our Seldon Deployment is live, we are able to interact with it through its API.\n",
    "\n",
    "There are two options in which we can interact with our new model. These are:\n",
    "\n",
    "a) Using CURL from the CLI (or another rest client like Postman)\n",
    "\n",
    "b) Using the Python SeldonClient\n",
    "\n",
    "#### a) Using CURL from the CLI"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "{\n",
      "  \"meta\": {\n",
      "    \"puid\": \"bvj1rjiq3vvnieo0oir4h7bf6f\",\n",
      "    \"tags\": {\n",
      "    },\n",
      "    \"routing\": {\n",
      "    },\n",
      "    \"requestPath\": {\n",
      "      \"classifier\": \"reddit-classifier:0.1\"\n",
      "    },\n",
      "    \"metrics\": []\n",
      "  },\n",
      "  \"data\": {\n",
      "    \"names\": [\"t:0\", \"t:1\"],\n",
      "    \"ndarray\": [[0.6815614604065544, 0.3184385395934456]]\n",
      "  }\n",
      "}"
     ]
    },
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current\n",
      "                                 Dload  Upload   Total   Spent    Left  Speed\n",
      "\r",
      "  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0\r",
      "100   372  100   300  100    72   1522    365 --:--:-- --:--:-- --:--:--  1897\n"
     ]
    }
   ],
   "source": [
    "%%bash\n",
    "curl -X POST -H 'Content-Type: application/json' \\\n",
    "    -d \"{'data': {'names': ['text'], 'ndarray': ['Hello world this is a test']}}\" \\\n",
    "    http://127.0.0.1/seldon/default/reddit-classifier/api/v0.1/predictions"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### b) Using the Python SeldonClient"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 70,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Success:True message:\n",
      "Request:\n",
      "data {\n",
      "  names: \"text\"\n",
      "  ndarray {\n",
      "    values {\n",
      "      string_value: \"Hello world this is a test\"\n",
      "    }\n",
      "  }\n",
      "}\n",
      "\n",
      "Response:\n",
      "meta {\n",
      "  puid: \"uld2famhfrb97vd7regu0q7k32\"\n",
      "  requestPath {\n",
      "    key: \"classifier\"\n",
      "    value: \"reddit-classifier:0.1\"\n",
      "  }\n",
      "}\n",
      "data {\n",
      "  names: \"t:0\"\n",
      "  names: \"t:1\"\n",
      "  ndarray {\n",
      "    values {\n",
      "      list_value {\n",
      "        values {\n",
      "          number_value: 0.6815614604065544\n",
      "        }\n",
      "        values {\n",
      "          number_value: 0.3184385395934456\n",
      "        }\n",
      "      }\n",
      "    }\n",
      "  }\n",
      "}\n",
      "\n"
     ]
    }
   ],
   "source": [
    "from seldon_core.seldon_client import SeldonClient\n",
    "import numpy as np\n",
    "\n",
    "host = \"localhost\"\n",
    "port = \"80\" # Make sure you use the port above\n",
    "batch = np.array([\"Hello world this is a test\"])\n",
    "payload_type = \"ndarray\"\n",
    "deployment_name=\"reddit-classifier\"\n",
    "transport=\"rest\"\n",
    "namespace=\"default\"\n",
    "\n",
    "sc = SeldonClient(\n",
    "    gateway=\"ambassador\", \n",
    "    ambassador_endpoint=host + \":\" + port,\n",
    "    namespace=namespace)\n",
    "\n",
    "client_prediction = sc.predict(\n",
    "    data=batch, \n",
    "    deployment_name=deployment_name,\n",
    "    names=[\"text\"],\n",
    "    payload_type=payload_type,\n",
    "    transport=\"rest\")\n",
    "\n",
    "print(client_prediction)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7) Clean your environment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!kubectl delete -f reddit_clf.json"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 71,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "release \"ambassador\" deleted\r\n"
     ]
    }
   ],
   "source": [
    "!helm del --purge ambassador"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 72,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "release \"seldon-core-operator\" deleted\r\n"
     ]
    }
   ],
   "source": [
    "!helm del --purge seldon-core-operator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
